#include <cassert>
#include <cstring>
#include "miniutil/buffer_alloc.h"
#include "miniutil/fifobuf.h"

namespace miniutil { namespace data {

	fifobuf::fifobuf(int size)
	{
		buflen = size;
        if(size > 0)
        {
		    mpbuf = miniutil::data::pool_buffer_alloc::getdefaultinstance()->retrieve_mem(buflen);
        }
		contentpos.first = contentpos.second = 0;
	}

	fifobuf::~fifobuf()
	{
		if(mpbuf != NULL)
		{
			miniutil::data::pool_buffer_alloc::getdefaultinstance()->release_mem(mpbuf);
		}
	}

	fifobuf::fifobuf(const fifobuf& other)
	{
		buflen = other.buflen;
        if(buflen > 0)
        {
		    mpbuf = miniutil::data::pool_buffer_alloc::getdefaultinstance()->retrieve_mem(buflen);
		    memcpy(mpbuf, other.mpbuf, buflen);
        }
		contentpos = other.contentpos;
	}
	
	const fifobuf& fifobuf::operator =(const fifobuf& other)
	{
        if(this == &other)
            return (*this);

		if(other.buflen != buflen)
		{
            if(mpbuf != NULL)
			    miniutil::data::pool_buffer_alloc::getdefaultinstance()->release_mem(mpbuf);
			buflen = other.buflen;
			mpbuf = miniutil::data::pool_buffer_alloc::getdefaultinstance()->retrieve_mem(buflen);
		}
		memcpy(mpbuf, other.mpbuf, buflen);
		contentpos = other.contentpos;
		return *this;
	}

	int fifobuf::read(char* pbuf, int len)
	{
		int finallen = 0;
		if(contentpos.second == 0)
			return -1;
		if((int)contentpos.second > len)
			finallen = len;
		else
			finallen = contentpos.second;
			
        if(pbuf != NULL)
        {
        		if((int)(buflen-contentpos.first) >= finallen)
        		{
        			memcpy(pbuf, mpbuf+contentpos.first, finallen);

        		}
        		else
        		{
        			int lstlen = buflen-contentpos.first;
        			memcpy(pbuf, mpbuf+contentpos.first, lstlen);
        			memcpy(pbuf+lstlen, mpbuf, finallen-lstlen);
        		}		
        }
    
		contentpos.second -= finallen;
		if(contentpos.second == 0) //reset
			contentpos.first = 0;
		else
		{
			contentpos.first += finallen;
			contentpos.first %= buflen;
		}
		return finallen;
	}

	int fifobuf::getwritepos(std::pair<char*, unsigned int>& writepos)
	{
		if(buflen == contentpos.second)
		{
			writepos.first = mpbuf;
			writepos.second = 0;
			return -1;
		}
			
		if(contentpos.second == 0)
		{
			writepos.first = mpbuf;
			writepos.second = buflen;
			return buflen;
		}
		
		unsigned int frompos = (contentpos.first+contentpos.second)%buflen;
		int len;
		if(frompos <= contentpos.first)
			len = contentpos.first - frompos;
		else
			len = buflen - frompos;

        assert(frompos + len <= buflen);

		writepos.first = mpbuf+frompos;
		writepos.second = len;
		return len;
	}

	void fifobuf::more_content(int len)
	{
        int offset = (contentpos.first+contentpos.second)%buflen;
		contentpos.second += len; 
        if((int)contentpos.second > buflen)
		    assert(0);
	}

	bool fifobuf::have_content()
	{
		return contentpos.second > 0;
	}

	bool fifobuf::have_space(int minsize)
	{
		if(buflen - (int)contentpos.second >= minsize)
			return true;
		else
			return false;
	}
	
	int fifobuf::clear_content()
	{
	    contentpos.first = contentpos.second = 0;
	    return 0;
	}
	  
    int fifobuf::set_buffer_size(int newbufsize)
    {
        //char* pnewbuf = new char[newbufsize];
        char* pnewbuf = miniutil::data::pool_buffer_alloc::getdefaultinstance()->retrieve_mem(newbufsize);
        memcpy(pnewbuf, mpbuf, newbufsize>buflen?buflen:newbufsize);
        if(mpbuf != NULL)
		    miniutil::data::pool_buffer_alloc::getdefaultinstance()->release_mem(mpbuf);
        mpbuf = pnewbuf;
        buflen = newbufsize;
        return 0;
    }
} }
	